/**
 * Class: DVertex
 *
 * Purpose: 
 *
 * Created: Feb 21, 2010 @ 2:08:42 PM
 * @author Ken
 */
package org.openfantasia.util.math.geometry;

import org.openfantasia.util.math.DPoint3;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class DPolygon {

	private List<DEdge> edges = new ArrayList<DEdge>();

	private boolean closed = true;


	public DPolygon() {
	}


	public DPolygon(boolean closed) {
		this.closed = closed;
	}


	public DPolygon(DEdge... edges) {
		this.edges.addAll(Arrays.asList(edges));
	}


	public DPolygon(boolean closed, DEdge... edges) {
		this.closed = closed;
		this.edges.addAll(Arrays.asList(edges));
	}


	public DPolygon(DPolygon that) {
		copy(that);
	}


	public DPolygon copy() {
		return new DPolygon(this);
	}


	public void copy(DPolygon that) {
		this.closed = that.closed;
		setEdges(that.edges);
	}


	public List<DEdge> getEdges() {
		List<DEdge> result = new ArrayList<DEdge>(edges);
		DPoint3 p0 = result.get(0).getP0();
		DPoint3 p1 = result.get(result.size() - 1).getP1();
		if (closed && !p0.equals(p1)) {
			result.add(new DEdge(p1, p0));
		}
		return result;
	}


	public void setEdges(List<DEdge> edges) {
		this.edges.clear();
		for (DEdge edge : edges) {
			this.edges.add(edge.copy());
		}
	}


	public List<DPoint3> getVertices() {
		List<DPoint3> result = new ArrayList<DPoint3>();
		DPoint3 p;

		List<DEdge> edges = getEdges();
		for (DEdge edge : edges) {
			p = edge.getP0();
			result.add(p);
		}
		return result;
	}


	public boolean isClosed() {
		return closed;
	}


	public void setClosed(boolean closed) {
		this.closed = closed;
	}


	public void add(DEdge edge) {
		if (!edges.contains(edge)) {
			edges.add(edge);
		}
	}


	public void remove(DEdge edge) {
		edges.remove(edge);
	}


	public DPolygon offset(DPoint3 off) {
		for (DEdge seg : edges) {
			seg.translate(off);
		}
		return this;
	}


	public DPolygon scale(double scale) {
		for (DEdge seg : edges) {
			seg.scale(scale);
		}
		return this;
	}


	public DPolygon scale(DPoint3 scale) {
		for (DEdge seg : edges) {
			seg.scale(scale);
		}
		return this;
	}


	@Override
	public boolean equals(Object o) {
		if (this == o) {
			return true;
		}
		if (!(o instanceof DPolygon)) {
			return false;
		}

		DPolygon that = (DPolygon) o;

		return equivalent(this.edges, that.edges);
	}


	@Override
	public int hashCode() {
		int result = edges != null ? edges.hashCode() : 0;
		return result;
	}


	private boolean equivalent(List<DEdge> srcA, List<DEdge> srcB) {
		if (srcA == null) {
			return false;
		}
		if (srcB == null) {
			return false;
		}
		if (srcA == srcB) {
			return true;
		}
		if (srcA.size() != srcB.size()) {
			return false;
		}
		if (srcA.equals(srcB)) {
			return true;
		}

		boolean result = false;
		List<DEdge> a = srcA;
		int len = a.size();

		// forward check
		for (int i = 0; !result && (i < len); i++) {
			List<DEdge> b = new ArrayList<DEdge>(srcB);
			Collections.rotate(b, i);
			result = a.equals(b);
			if (!result) {
				Collections.reverse(b);
				result = a.equals(b);
			}
		}

		return result;
	}


	@Override
	public String toString() {
		return "DPolygon{" +
				"closed=" + closed +
				", edges=" + edges +
				'}';
	}
}